package com.google.code.p.restrpc.client.loadbalance;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.code.p.restrpc.RpcRequest;
import com.google.code.p.restrpc.ServiceNode;

public final class LoadBalanceStrategyManager {
    
    private static final Log logger = LogFactory.getLog(LoadBalanceStrategyManager.class);
    
    static class LoadBalanceStrategyFactoryHolder {
        static LoadBalanceStrategyManager instance = new LoadBalanceStrategyManager();
    }
    
    /**
     * @return
     */
    public static LoadBalanceStrategyManager getInstance() {
        return LoadBalanceStrategyFactoryHolder.instance;
    }
    
    private Map<String, LoadBalanceStrategy> strategies = new HashMap<String, LoadBalanceStrategy>();
    
    private LoadBalanceStrategyManager() {
        InputStream is = null;
        try {
            is = LoadBalanceStrategyManager.class.getResourceAsStream("/META-INF/configuration/loadbalance.strategy.properties");
            if (is == null) {
                throw new LoadBalanceStrategyConfigurationException("/META-INF/configuration/loadbalance.strategy.properties not found!");
            }
            Properties properties = new Properties();
            properties.load(is);
            initStrategies(properties);
        }
        catch (IOException e) {
            throw new LoadBalanceStrategyConfigurationException(e);
        }
        finally {
            if (null != is) {
                try {
                    is.close();
                }
                catch (Exception e) {
                }
            }
        }
    }
    
    private void initStrategies(Properties properties) {
        for (Iterator iterator = properties.entrySet().iterator(); iterator.hasNext();) {
            try {
                Map.Entry<String, String> entry = (Map.Entry<String, String>) iterator.next();
                String className = entry.getValue();
                if (StringUtils.isEmpty(className)) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Caution:no implementation class specified for " + entry.getKey());
                    }
                    continue;
                }
                Class strategyClass = Class.forName(className);
                
                Object strategyInstance = strategyClass.newInstance();
                if (!(strategyInstance instanceof LoadBalanceStrategy)) {
                    throw new LoadBalanceStrategyConfigurationException("The class for " + entry.getKey()
                                                                        + " is not implemented "
                                                                        + LoadBalanceStrategy.class.getName());
                }
                strategies.put(entry.getKey(),
                               (LoadBalanceStrategy) strategyInstance);
            }
            catch (ClassNotFoundException e) {
                throw new LoadBalanceStrategyConfigurationException(e);
            }
            catch (InstantiationException e) {
                throw new LoadBalanceStrategyConfigurationException(e);
            }
            catch (IllegalAccessException e) {
                throw new LoadBalanceStrategyConfigurationException(e);
            }
        }
    }
    
    /**
     * @param strategyName
     * @return
     */
    public LoadBalanceStrategy getStrategy(String strategyName) {
        return strategies.get(strategyName);
    }
    
    /**
     * @param loadBalance
     * @return
     */
    public LoadBalanceStrategy getStrategy(LoadBalance loadBalance) {
        return getStrategy(loadBalance.value());
    }
    
    public static void main(String[] args) {
        LoadBalanceStrategy strategy = LoadBalanceStrategyManager.getInstance()
                                                                 .getStrategy(LoadBalance.RoundRobin);
        for (int i = 0; i < 100; i++) {
            System.out.println(i + ":"
                               + strategy.select("testService",
                                                 "testMethod",
                                                 new RpcRequest[4]));
        }
    }
}
